# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

import re

from collections import Counter
from contextlib import contextmanager
from functools import partial

from odoo.tests import common, new_test_user

survey_new_test_user = partial(new_test_user, context={'mail_create_nolog': True, 'mail_create_nosubscribe': True, 'mail_notrack': True, 'no_reset_password': True})


class SurveyCase(common.SavepointCase):
    def setUp(self):
        super(SurveyCase, self).setUp()

        """ Some custom stuff to make the matching between questions and answers
          :param dict _type_match: dict
            key: question type
            value: (answer type, answer field_name)
        """
        self._type_match = {
            'free_text': ('free_text', 'value_free_text'),
            'textbox': ('text', 'value_text'),
            'numerical_box': ('number', 'value_number'),
            'date': ('date', 'value_date'),
            'simple_choice': ('suggestion', 'value_suggested'),  # TDE: still unclear
            'multiple_choice': ('suggestion', 'value_suggested'),  # TDE: still unclear
            'matrix': ('suggestion', ('value_suggested', 'value_suggested_row')),  # TDE: still unclear
        }

        """ Create test data: a survey with some pre-defined questions and various test users for ACL """
        self.survey_manager = survey_new_test_user(
            self.env, name='Gustave Doré', login='survey_manager', email='survey.manager@example.com',
            groups='survey.group_survey_manager,base.group_user'
        )

        self.survey_user = survey_new_test_user(
            self.env, name='Lukas Peeters', login='survey_user', email='survey.user@example.com',
            groups='survey.group_survey_user,base.group_user'
        )

        self.user_emp = survey_new_test_user(
            self.env, name='Eglantine Employee', login='user_emp', email='employee@example.com',
            groups='base.group_user', password='user_emp'
        )

        self.user_portal = survey_new_test_user(
            self.env, name='Patrick Portal', login='user_portal', email='portal@example.com',
            groups='base.group_portal'
        )

        self.user_public = survey_new_test_user(
            self.env, name='Pauline Public', login='user_public', email='public@example.com',
            groups='base.group_public'
        )

        self.customer = self.env['res.partner'].create({
            'name': 'Caroline Customer',
            'email': 'customer@example.com',
        })

        self.survey = self.env['survey.survey'].with_user(self.survey_manager).create({
            'title': 'Test Survey',
            'access_mode': 'public',
            'users_login_required': True,
            'users_can_go_back': False,
            'state': 'open',
        })
        self.page_0 = self.env['survey.question'].with_user(self.survey_manager).create({
            'title': 'First page',
            'survey_id': self.survey.id,
            'sequence': 1,
            'is_page': True,
        })
        self.question_ft = self.env['survey.question'].with_user(self.survey_manager).create({
            'title': 'Test Free Text',
            'survey_id': self.survey.id,
            'sequence': 2,
            'question_type': 'free_text',
        })
        self.question_num = self.env['survey.question'].with_user(self.survey_manager).create({
            'title': 'Test NUmerical Box',
            'survey_id': self.survey.id,
            'sequence': 3,
            'question_type': 'numerical_box',
        })

    @contextmanager
    def with_user(self, user):
        """ Quick with_user environment """
        old_uid = self.uid
        try:
            self.uid = user.id
            self.env = self.env(user=self.uid)
            yield
        finally:
            # back
            self.uid = old_uid
            self.env = self.env(user=self.uid)

    def assertAnswer(self, answer, state, page):
        self.assertEqual(answer.state, state)
        self.assertEqual(answer.last_displayed_page_id, page)

    def assertAnswerLines(self, page, answer, answer_data):
        """ Check answer lines.

          :param dict answer_data:
            key = question ID
            value = {'value': [user input]}
        """
        lines = answer.user_input_line_ids.filtered(lambda l: l.page_id == page)
        answer_count = sum(len(user_input['value']) for user_input in answer_data.values())
        self.assertEqual(len(lines), answer_count)
        for qid, user_input in answer_data.items():
            answer_lines = lines.filtered(lambda l: l.question_id.id == qid)
            question = answer_lines[0].question_id  # TDE note: might have several answers for a given question
            if question.question_type == 'multiple_choice':
                values = user_input['value']
                answer_fname = self._type_match[question.question_type][1]
                self.assertEqual(
                    Counter(getattr(line, answer_fname).id for line in answer_lines),
                    Counter(values))
            elif question.question_type == 'simple_choice':
                [value] = user_input['value']
                answer_fname = self._type_match[question.question_type][1]
                self.assertEqual(getattr(answer_lines, answer_fname).id, value)
            elif question.question_type == 'matrix':
                [value_col, value_row] = user_input['value']
                answer_fname_col = self._type_match[question.question_type][1][0]
                answer_fname_row = self._type_match[question.question_type][1][1]
                self.assertEqual(getattr(answer_lines, answer_fname_col).id, value_col)
                self.assertEqual(getattr(answer_lines, answer_fname_row).id, value_row)
            else:
                [value] = user_input['value']
                answer_fname = self._type_match[question.question_type][1]
                self.assertEqual(getattr(answer_lines, answer_fname), value)

    def assertResponse(self, response, status_code, text_bits=None):
        self.assertEqual(response.status_code, status_code)
        for text in text_bits or []:
            self.assertIn(text, response.text)

    def _add_question(self, page, name, qtype, **kwargs):
        constr_mandatory = kwargs.pop('constr_mandatory', True)
        constr_error_msg = kwargs.pop('constr_error_msg', 'TestError')

        sequence = kwargs.pop('sequence', False)
        if not sequence:
            sequence = page.question_ids[-1].sequence + 1 if page.question_ids else page.sequence + 1

        base_qvalues = {
            'sequence': sequence,
            'title': name,
            'question_type': qtype,
            'constr_mandatory': constr_mandatory,
            'constr_error_msg': constr_error_msg,
        }
        if qtype in ('simple_choice', 'multiple_choice'):
            base_qvalues['labels_ids'] = [
                (0, 0, {
                    'value': label['value'],
                    'answer_score': label.get('answer_score', 0),
                    'is_correct': label.get('is_correct', False)
                }) for label in kwargs.pop('labels')
            ]
        elif qtype == 'matrix':
            base_qvalues['matrix_subtype'] = kwargs.pop('matrix_subtype', 'simple')
            base_qvalues['labels_ids'] = [
                (0, 0, {'value': label['value'], 'answer_score': label.get('answer_score', 0)})
                for label in kwargs.pop('labels')
            ]
            base_qvalues['labels_ids_2'] = [
                (0, 0, {'value': label['value'], 'answer_score': label.get('answer_score', 0)})
                for label in kwargs.pop('labels_2')
            ]
        else:
            pass
        base_qvalues.update(kwargs)
        question = self.env['survey.question'].create(base_qvalues)
        return question

    def _add_answer(self, survey, partner, **kwargs):
        base_avals = {
            'survey_id': survey.id,
            'partner_id': partner.id if partner else False,
            'email': kwargs.pop('email', False),
            'input_type': 'manually',
        }
        base_avals.update(kwargs)
        return self.env['survey.user_input'].create(base_avals)

    def _add_answer_line(self, question, answer, answer_value, **kwargs):
        qtype = self._type_match.get(question.question_type, (False, False))
        answer_type = kwargs.pop('answer_type', qtype[0])
        answer_fname = kwargs.pop('answer_fname', qtype[1])

        base_alvals = {
            'user_input_id': answer.id,
            'question_id': question.id,
            'skipped': False,
            'answer_type': answer_type,
        }
        base_alvals[answer_fname] = answer_value
        base_alvals.update(kwargs)
        return self.env['survey.user_input_line'].create(base_alvals)

    def _access_start(self, survey):
        return self.url_open('/survey/start/%s' % survey.access_token)

    def _access_page(self, survey, token):
        return self.url_open('/survey/fill/%s/%s' % (survey.access_token, token))

    def _access_submit(self, survey, token, post_data):
        return self.url_open('/survey/submit/%s/%s' % (survey.access_token, token), data=post_data)

    def _find_csrf_token(self, text):
        csrf_token_re = re.compile("(input.+csrf_token.+value=\")([_a-zA-Z0-9]{51})", re.MULTILINE)
        return csrf_token_re.search(text).groups()[1]
